fix(verify,qa): 从 host app 解析企业版 organizations 包 —— 共享解析器落到 @objectstack/types/node (#4700) - #4857
Conversation
…st app (#4700) Node ESM resolves a bare `import()` against the importer's own realpath. `packages/verify`'s realpath is inside the framework workspace, while `@objectstack/organizations` is cloud-private and only ever lives in the verified app's `node_modules` — so `bootStack({ multiTenant: true })` fell into its catch on every real host app and told operators to install a package they had already installed. Same defect class as cloud#1013 (#4699 fixed `serve`). Move the host-app resolver out of the CLI, where verify and the dogfood suite could not import it without inverting the dependency direction, into a node-only subpath export of @objectstack/types: - `@objectstack/types/node` — new subpath, NOT the root export: the root is a dependency of the edge-targeted `@objectstack/hono` and reaches zero `node:` builtins. tsup emits both entries self-contained (`splitting: false`), and node-isolation.test.ts walks the root import graph and fails on the first reachable `node:` specifier, so the split is enforced, not just intended. - CLI consumes it; its private copy and that copy's tests are deleted. - `BootOptions.hostRoot` (optional, defaults to cwd) names the app supplying the optional packages. - Dogfood: both multi-org probes were CONSTANT-FALSE by construction, so the #1994 cross-tenant RLS gate and the attachments isolation block had never executed while the suite was green. They now resolve like the runtime does, and OS_TEST_MULTI_ORG_ENABLED=1 turns an unexpected skip into a hard failure. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny
…t-resolver-shared
…t-resolver-shared
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 4 package(s): 23 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
复核通过 —— ACCEPT,已标 ready 并送合并队列本单是接手一个被宿主上下文压缩机械性中断的 agent 的成果(那次中断与任务本身无关)。接手方做了正确的事:通读上一版的实质提交、确认四条验收全覆盖、不推倒重来也不做无谓改动,然后补上前一版一次都没跑过的验证。 验收第 4 条 —— 我派发时说"这是最像测试在说谎的一处",它被真正回答了我要求的是:修好之后那条分支是否真的跑起来了,跑起来之后是过还是不过。不是"我改了解析器所以应该好了"。 dev 在 boot 日志里 并且它把这条路堵死了:叠了一个声明式开关 这正是 Prime Directive #10 要的形状:一个恒假的探测比没有探测更糟,而现在"声称要跑却跑不了"会当场红掉,不再是静默 skip。数字也对得上:全量 82 文件 反空洞验证也做了把 隔离是结构性的,不是靠自觉
changeset 等级正确
边界守住了
一处判断我认可它没有立单
Generated by Claude Code |
Fixes #4700
承接 #4699 / cloud#1013 的同一缺陷类:framework 侧的包用裸
import()加载 host app 提供的可选包。Node ESM 对裸 specifier 按导入方自己的 realpath 解析,而packages/verify、packages/qa/dogfood的 realpath 都在 framework 工作区里;@objectstack/organizations是 cloud 私有包,只存在于被验证/被测 app 的node_modules。于是这两处永远解析不到。共享位置:
@objectstack/types/node(新增公开导出面)createHostRequire/createHostImporter从packages/cli/src/utils/import-from-host.ts迁到packages/types/src/node.ts,CLI 改为消费它,私有副本连同其测试一并删除 —— 一个行为一个源。为什么是子路径而不是根导出。
@objectstack/types是@objectstack/hono的依赖,后者的自我定位是 "edge-compatible REST API server for Cloudflare Workers, Deno, Bun, and Node";LiteKernel在 Workers 上启动的 plugin/service 层同样依赖它。根入口今天可达的node:内建数为 0,这是那些消费方赖以成立的性质:Workers bundle 里出现node:module,哪怕一次都不调用也会构建失败。而这里的东西按定义必须用node:module/node:url(它存在的意义就是驱动 Node 自己的解析器),所以它住在自己的入口后面。隔离是结构性的,不是约定性的:
packages/types/tsup.config.ts把src/index.ts与src/node.ts编成两个独立入口,splitting: false,根 bundle 里不含对 node 切片的任何引用(与@objectstack/metadata已经在用的./node形状完全一致,配置也逐字相同,只多一个 entry)。packages/types/src/node-isolation.test.ts走真实的 import 图,从src/index.ts出发,遇到第一个可达的node:specifier 就失败;并且额外钉住「src/node.ts确实还在用 node 内建」,避免解析器哪天不用 node 内建了、前两条空转通过、子路径看起来仍然正当。实测产物:
dist/index.mjs中node:出现 0 次,dist/node.mjs则是完整的 node 切片。bootStack/objectstack verify --multi-tenantharness.ts改用共享解析器,并新增BootOptions.hostRoot(可选,默认process.cwd())指明「提供可选包的那个 app」。报错文案也从 "Install/link it in this workspace"(指向 framework checkout —— 唯一装了也没用的地方)改成点名 app 目录的 "Install/link it in THIS APP (路径)"。qa dogfood:两条从没跑过的门(验收第 4 条)
两个 dogfood 文件各抄了一份同样的裸
import()探测,所以organizationsAvailable是恒假的 —— 不是「因为包不在所以假」,而是构造上就不可能为真,在任何环境下都如此,包括注释里声称「会跑这条」的 cloud CI。结果是 #1994 跨租户 RLS 证明、以及 attachments 跨租户隔离 (g) 两个 block 从未执行过,而套件一直是绿的。这就是 Prime Directive #10「declared ≠ enforced」的测试版本,一个恒假的能力探测比没有探测更糟 —— 它制造了覆盖率的假象。packages/qa/dogfood/test/enterprise-organizations.ts取代了这两份副本:@objectstack/types/node,探测因此能够为真了。OS_TEST_MULTI_ORG_ENABLED=1声明「这一次运行本应带着这个包」。声明了却解析不到 = 硬失败,而不是静默跳过;没声明且解析不到才跳过,且警告里点名了那个开关。诚实地说明现状:在 framework 仓里这个 cloud 私有包确实装不上,所以这两条门在本仓仍然 skip —— 这是正确且不可避免的。改变的是,skip 现在是「环境的事实」而不是「解析器的产物」,而带着包的 cloud/enterprise 运行会真的执行这两个 block(并且在它以为自己带了包却没带时大声失败)。
与 #4719 的关系(重要)
#4719 说的是
createHostRequire认NODE_PATH、于是「host app 必须自己声明」这条契约在 pnpm 工作区里没被强制。本 PR 不碰那个行为:两个函数是逐字搬运的,可执行代码与origin/main上的版本完全一致(diff 只有注释措辞:CLI → importing package)。#4719 的缺陷既没被修,也没被放大 —— 只是换了住址。修它的时候请落在packages/types/src/node.ts,那里现在是唯一的源。验证
全部在共享 verify 锁下、
--max-old-space-size=4096、--maxWorkers=2、按包--filter跑。pnpm --filter @objectstack/types buildpnpm --filter @objectstack/types typecheckpnpm --filter @objectstack/types testpnpm --filter @objectstack/verify typecheckpnpm --filter @objectstack/verify testpnpm --filter @objectstack/verify buildpnpm --filter @objectstack/cli typecheckpnpm --filter @objectstack/cli testpnpm --filter @objectstack/dogfood test(全量 82 文件)eslint --no-inline-config(改动文件)pnpm install --frozen-lockfilecheck-changeset-fixed/check-override-consistency新测试不是空转的 —— 实测过。 把
harness.ts临时改回裸import()后重跑:app 目录里明明装着这个包,报错却说找不到 —— 这正是 issue 描述的症状,逐字复现。改回修复后三条全绿。
验收第 2 条(
vi.mock仍然生效)实测确认:harness.posture.test.ts3 条全绿 —— host 解析不到 → 回退裸 import → mock 命中,这条链路没被改动破坏。验收第 4 条(那条分支到底跑没跑起来)实测确认:在
packages/qa/dogfood/node_modules/里放一个一次性替身包(不入库,实验后已删除),让探测得以为真:也就是说:那两条分支确实跑起来了,并且是通过的(boot 日志里
posture: "isolated",证明围墙姿态真的生效了)。反向也钉住了 —— 移除替身包后带OS_TEST_MULTI_ORG_ENABLED=1重跑,如期硬失败:不带该环境变量时按预期 skip,并打印点名开关的 stderr 警告(默认 reporter 会折叠全跳过文件的 stderr,
--reporter=verbose下可见 —— 这是 vitest 的 reporter 行为,与改动无关;真正的强制手段是上面那条硬失败,它不依赖任何控制台输出)。全量 dogfood 里那 1 skipped 文件 / 3 skipped tests,正是上面说的两条 enterprise 门(RLS 2 条 + attachments (g) 1 条)—— 数字对得上,没有别的东西在偷偷跳过。
顺带发现(未在本 PR 修)
packages/qa/dogfood的 tsconfig 没人跑 —— 14 个类型错误积在一个「声明了但未执行」的门后面 #4855 ——packages/qa/dogfood有一份strict+NodeNext的tsconfig.json,但package.json里没有typecheckscript,所以它从不进turbo run typecheck(三个 qa 包里唯一漏掉的一个),背后已经积了 14 个类型错误。已确认全部先于本 PR 存在(本 PR 新增的两个文件tsc --noEmit干净),按 Prime Directive chore: version packages #10 单独归档,未夹带修复。packages/spec/**零改动。🤖 Generated with Claude Code
https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny
Generated by Claude Code